package snippets.cse524.activeDates;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringReader;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.text.html.MinimalHTMLWriter;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.iterators.IteratorEnumeration;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;

import static snippets.utils.Utils.*;

public class WikipediaDateExtractor {
	private static final String DATE_REGEX =
	// "((in|on|the)\\s+)?(\\d\\d?([a-z]{2})?\\s+)?((of\\s+)?\\w{3,},?\\s+)?(\\d{4})"

	// A more elaborate regex
	// "\\W((in|on|the)\\s+)?(\\d\\d?((th|rd))?\\s+)?((of\\s+)?(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\\w+,?\\s+)?([0-9]{4})\\W"

	// Four-digit years
	"\\W([0-9]{4})\\W";

	private static final Pattern DATE_PATTERN = Pattern.compile(DATE_REGEX);

	private static final int INFOBOX_BEGIN_THRESHOLD = 50;
	private static final String INFOBOX_BEGIN_SENTINEL_PREFIX = "{{Infobox";
	private static final String INFOBOX_END_SENTINEL = "}}";

	private static final Pattern OFFICE_KEY_PREFIX = Pattern
			.compile("\\|\\s*name\\s*=");
	private static final Pattern TERM_START_KEY_PREFIX = Pattern
			.compile("\\|\\s*term_start\\s*=");
	private static final Pattern TERM_END_KEY_PREFIX = Pattern
			.compile("\\|\\s*term_end\\s*=");
	private static final Pattern SUCC_KEY_PREFIX = Pattern
			.compile("\\|\\s*successor\\s*=");
	private static final Pattern PRED_KEY_PREFIX = Pattern
			.compile("\\|\\s*predecessor\\s*=");

	private static final Pattern NAME_KEY_PREFIX = Pattern
			.compile("\\|\\s*name\\s*=");

	private static final Pattern BIRTH_DATE_KEY_PREFIX = Pattern
			.compile("\\|\\s*birth_date\\s*=");
	private static final Pattern DEATH_DATE_KEY_PREFIX = Pattern
			.compile("\\|\\s*death_date\\s*=");

	private static final Pattern INFOBOX_DATE_PATTERN = Pattern
			.compile("(\\|\\d+)+");

	// private static final Pattern SSBOX_START_PATTERN =
	// Pattern.compile("\\s*\\{\\{s-start\\}\\}\\s*");
	// private static final Pattern SSBOX_BEFORE_PATTERN =
	// Pattern.compile("\\{{s-bef.*?(\\|rows=(\\d+))?.*?\\|before=([^\\|\\}]+).*?}}");
	// private static final Pattern SSBOX_TITLE_PATTERN =
	// Pattern.compile("\\{{s-ttl.*?(\\|rows=(\\d+))?.*?\\|title=([^\\|\\}]+).*?\\|years=(\\d+).(\\d+).*?}}");
	// private static final Pattern SSBOX_AFTER_PATTERN =
	// Pattern.compile("\\{{s-aft.*?\\|after=([^\\|\\}]+).*?}}");
	// private static final Pattern SSBOX_INCUMBENT_PATTERN =
	// Pattern.compile("\\{{s-bef.*?(\\|rows=(\\d+))?.*?\\|before=([^\\|\\}]+).*?}}");
	// private static final Pattern SSBOX_END_PATTERN =
	// Pattern.compile("\\s*\\{\\{s-end\\}\\}\\s*");

	private static final String SSBOX_START = "{{s-start}}";
	private static final String SSBOX_END = "{{s-end}}";

	private static final String[] BEFORE_PATTERNS = { "s-bef" };
	private static final String[] TITLE_PATTERNS = { "s-ttl", "s-tul" };
	private static final String[] AFTER_PATTERNS = { "s-aft", "s-inc", "s-non",
			"s-vac" };

	// We look for these keys, and try to guess whether this page belongs to a
	// person
	// This should not be too strict
	private static final Pattern[] PERSON_INDICATOR_KEYS = { NAME_KEY_PREFIX,
			BIRTH_DATE_KEY_PREFIX };

	private static final Pattern[] PERSON_INFO_KEYS = { NAME_KEY_PREFIX,
			BIRTH_DATE_KEY_PREFIX, DEATH_DATE_KEY_PREFIX };

	private static final int DEFAULT_MONTH = 1;
	private static final int DEFAULT_DAY = 1;

	private final String wikiArticleText;
	private final String wikiTitle;
	private final Map<Pattern, String> values;
	Boolean isEntityCache = null;

	private static Map<String, Pair<GregorianCalendar, GregorianCalendar>> knownLifeSpans = new HashMap<String, Pair<GregorianCalendar, GregorianCalendar>>();

	public WikipediaDateExtractor(String wikiArticleText, String title) {
		if (StringUtils.isEmpty(wikiArticleText)) {
			this.wikiArticleText = StringUtils.EMPTY;
		} else {
			String cleanWikiArticleText;

			try {
				cleanWikiArticleText = removeInvalidSections(wikiArticleText);
			} catch (Exception e) {
				// Y U NO GET CLEAN?
				cleanWikiArticleText = wikiArticleText;
			}

			this.wikiArticleText = cleanWikiArticleText;
		}
		this.values = new HashMap<Pattern, String>();
		this.wikiTitle = title;
	}

	private interface WeightFromPeriodCount {
		double getWeight(int beginYear, int endYear, double mentions,
				double totalMentions);
	}

	public List<SuccessionChain> processInfoboxChains() {
		Scanner articleScanner = new Scanner(new StringReader(wikiArticleText));
		java.util.List<SuccessionChain> chains = new ArrayList<SuccessionChain>();

		do {
			if (!articleScanner.hasNextLine()) {
				break;
			}

			int startLine = 0;
			for (; startLine < INFOBOX_BEGIN_THRESHOLD
					&& articleScanner.hasNextLine(); ++startLine) {
				String firstLine = articleScanner.nextLine();

				if (StringUtils.startsWithIgnoreCase(firstLine,
						INFOBOX_BEGIN_SENTINEL_PREFIX)) {
					break;
				}
			}

			if (startLine == INFOBOX_BEGIN_THRESHOLD
					|| !articleScanner.hasNextLine()) {
				break;
			}

			Map<String, String> kvps = new HashMap<String, String>();

			while (articleScanner.hasNextLine()) {
				String articleLine = articleScanner.nextLine();

				if (OFFICE_KEY_PREFIX.matcher(articleLine).find()
						|| StringUtils.startsWithIgnoreCase(articleLine,
								INFOBOX_END_SENTINEL)) {
					if (!kvps.isEmpty() && kvps.containsKey("title")) {

						String pred = kvps.containsKey("pred") ? kvps
								.get("pred") : "nobody";
						String succ = kvps.containsKey("succ") ? kvps
								.get("succ") : "nobody";
						String title = kvps.containsKey("title") ? kvps
								.get("title") : "O";
						String begin = kvps.containsKey("begin") ? kvps
								.get("begin") : "";
						String end = kvps.containsKey("end") ? kvps.get("end")
								: "nobody";

						chains.add(new SuccessionChain(
								new Predecessor(pred, 1), new Office(title, 1,
										begin, end), new Successor(succ, 1)));
					}

					kvps.clear();
				}

				if (StringUtils.startsWithIgnoreCase(articleLine,
						INFOBOX_END_SENTINEL))
					break;

				String[] parts = StringUtils.split(articleLine, '=');

				if (parts.length != 2) {
					continue;
				}

				String key = null;

				if (OFFICE_KEY_PREFIX.matcher(articleLine).find()) {
					key = "title";
				}

				if (TERM_START_KEY_PREFIX.matcher(articleLine).find()) {
					key = "begin";
				}

				if (TERM_END_KEY_PREFIX.matcher(articleLine).find()) {
					key = "end";
				}

				if (SUCC_KEY_PREFIX.matcher(articleLine).find()) {
					key = "succ";
				}

				if (PRED_KEY_PREFIX.matcher(articleLine).find()) {
					key = "pred";
				}

				if (key != null) {
					kvps.put(key, parts[1]);
				}
			}

		} while (false);

		return chains;
	}

	private static Map<String, WeightFromPeriodCount> weightFuncs = new HashMap<String, WikipediaDateExtractor.WeightFromPeriodCount>();
	private static Map<Integer, Double> yearWeights = new HashMap<Integer, Double>();

	static {
		// weightFuncs.put("1", new WeightFromPeriodCount() {
		// @Override
		// public double getWeight(int beginYear, int endYear, int mentions,
		// int totalMentions) {
		// return 1;
		// }
		// });
		// weightFuncs.put("k", new WeightFromPeriodCount() {
		// @Override
		// public double getWeight(int beginYear, int endYear, int mentions,
		// int totalMentions) {
		// return 1.0d / (double) (endYear - beginYear);
		// }
		// });
		weightFuncs.put("sqrt(k)", new WeightFromPeriodCount() {
			@Override
			public double getWeight(int beginYear, int endYear,
					double mentions, double totalMentions) {
				return 1.0d / Math.sqrt(endYear - beginYear);
			}
		});
		weightFuncs.put("k^1/E", new WeightFromPeriodCount() {
			@Override
			public double getWeight(int beginYear, int endYear,
					double mentions, double totalMentions) {
				return 1.0d / Math.pow(endYear - beginYear, 1.0d / Math.E);
			}
		});
		weightFuncs.put("k^1/phi", new WeightFromPeriodCount() {
			@Override
			public double getWeight(int beginYear, int endYear,
					double mentions, double totalMentions) {
				return 1.0d / Math.pow(endYear - beginYear, 1.0d / 1.61803399);
			}
		});
		// weightFuncs.put("frac * period", new WeightFromPeriodCount() {
		// @Override
		// public double getWeight(int beginYear, int endYear, int mentions,
		// int totalMentions) {
		// return (double) (endYear - beginYear) * (double) (mentions) /
		// (double) (totalMentions);
		// }
		// });

		try {
			Scanner s = new Scanner(new File(
					"/home/sandesh247/year_count_weights"));

			while (s.hasNextLine()) {
				String line = s.nextLine();
				String[] parts = StringUtils.split(line);

				yearWeights.put(Integer.parseInt(parts[0]),
						Double.parseDouble(parts[1]));
			}
		} catch (FileNotFoundException e) {
			// throw new RuntimeException(e);
		}

		try {
			Scanner s = new Scanner(
					new File("/home/sandesh247/known_lifespans"));

			while (s.hasNextLine()) {
				String line = s.nextLine();
				String[] parts = StringUtils.split(line, '\t');

				if (parts.length != 3) {
					continue;
				}

				try {
				knownLifeSpans
						.put(parts[0],
								new ImmutablePair<GregorianCalendar, GregorianCalendar>(
										new GregorianCalendar(Integer
												.parseInt(parts[1]), 1, 1),
										new GregorianCalendar(Integer
												.parseInt(parts[2]), 1, 1)));
				} catch (NumberFormatException ne) {
					// meh, what to do :(
					// bad lines shall be skipped
				}
			}
		} catch (FileNotFoundException e) {
			// throw new RuntimeException(e);
		}

	}

//	public WikipediaDateExtractor(File file) throws IOException {
//		this(readFile(file));
//	}

	public boolean isEntity() {
		if (isEntityCache != null) {
			return isEntityCache;
		}

		isEntityCache = false;
		String metaTitle = this.wikiTitle.replaceAll("\\ ", "_");
		
		if(knownLifeSpans.containsKey(metaTitle)) {
			isEntityCache = true;
			return isEntityCache;
		}

		// the do-ONCE-while loop enables us to simply break out, if this is not
		// an entity
		do {
			Scanner articleScanner = new Scanner(new StringReader(
					wikiArticleText));

			if (!articleScanner.hasNextLine()) {
				break;
			}

			int startLine = 0;
			for (; startLine < INFOBOX_BEGIN_THRESHOLD
					&& articleScanner.hasNextLine(); ++startLine) {
				String firstLine = articleScanner.nextLine();

				if (StringUtils.startsWithIgnoreCase(firstLine,
						INFOBOX_BEGIN_SENTINEL_PREFIX)) {
					break;
				}
			}

			if (startLine == INFOBOX_BEGIN_THRESHOLD
					|| !articleScanner.hasNextLine()) {
				break;
			}

			// NOTE: initialized to false by default
			boolean[] personIndicatorKeysFound = new boolean[PERSON_INDICATOR_KEYS.length];

			while (articleScanner.hasNextLine()) {
				String articleLine = articleScanner.nextLine();

				for (int i = 0; i < PERSON_INDICATOR_KEYS.length; ++i) {
					if (PERSON_INDICATOR_KEYS[i].matcher(articleLine).find()) {
						personIndicatorKeysFound[i] = true;
					}
				}

				for (int i = 0; i < PERSON_INFO_KEYS.length; ++i) {
					if (PERSON_INFO_KEYS[i].matcher(articleLine).find()) {
						this.values.put(PERSON_INFO_KEYS[i], articleLine);
					}
				}

				if (StringUtils.startsWithIgnoreCase(articleLine,
						INFOBOX_END_SENTINEL)) {
					isEntityCache = BooleanUtils.and(personIndicatorKeysFound);
					break;
				}
			}
		} while (false); // do this exactly once
		
		return isEntityCache;
	}

	public GregorianCalendar getBirthdate() {
		if (!isEntity()) {
			// throw Exception?
			return null;
		}
		
		String metaTitle = this.wikiTitle.replaceAll("\\ ", "_");
		
		if(knownLifeSpans.containsKey(metaTitle)) {
			return knownLifeSpans.get(metaTitle).getLeft();
		}

		return getPersonInfoBoxDate(values.get(BIRTH_DATE_KEY_PREFIX));
	}

	public GregorianCalendar getDeathdate() {
		if (!isEntity()) {
			// throw Exception?
			return null;
		}

		String metaTitle = this.wikiTitle.replaceAll("\\ ", "_");

		if(knownLifeSpans.containsKey(metaTitle)) {
			return knownLifeSpans.get(metaTitle).getRight();
		}

		if (values.containsKey(DEATH_DATE_KEY_PREFIX)) {
			GregorianCalendar deathDate = getPersonInfoBoxDate(values
					.get(DEATH_DATE_KEY_PREFIX));

			if (deathDate != null) {
				if (getBirthdate() != null && deathDate.after(getBirthdate())) {
					return deathDate;
				}
			}
		}

		GregorianCalendar birthDate = getBirthdate();

		if (null == birthDate) {
			return null;
		}

		birthDate.add(Calendar.YEAR, 100);

		return birthDate;
	}

	public GregorianCalendar[] getAllDates() {
		List<GregorianCalendar> dates = new ArrayList<GregorianCalendar>();

		Matcher matcher = DATE_PATTERN.matcher(wikiArticleText);

		while (matcher.find()) {
			dates.add(new GregorianCalendar(Integer.parseInt(matcher
					.group(matcher.groupCount())), DEFAULT_MONTH, DEFAULT_DAY));
		}

		return dates.toArray(new GregorianCalendar[] {});
	}

	public boolean adjustYearWeights = true;
	public double cutOffThreshold = 0.1;

	public Map<Integer, Double> getYearCounts() {
		if (getBirthdate() == null)
			return null;

		Integer birthYear = getBirthdate().get(Calendar.YEAR);
		Integer deathYear = getDeathdate().get(Calendar.YEAR);

		return getYearCounts(birthYear, deathYear);

	}

	private Map<Integer, Double> getYearCounts(Integer birthYear,
			Integer deathYear) {
		Map<Integer, Double> yearCounts = new HashMap<Integer, Double>();

		for (GregorianCalendar date : getAllDates()) {
			int year = date.get(Calendar.YEAR);

			if (year < birthYear || year > deathYear || year > 2011) {
				continue;
			}

			if (!yearCounts.containsKey(year)) {
				yearCounts.put(year, 0.0d);
			}

			yearCounts.put(year, yearCounts.get(year) + 1.0d);
		}

		// adjust the count for years with their weight
		if (adjustYearWeights) {
			Set<Integer> _years = new HashSet<Integer>(yearCounts.keySet());
			for (Integer year : _years) {
				if (yearWeights.containsKey(year)) {
					yearCounts.put(year,
							yearCounts.get(year) * yearWeights.get(year));
				} else {
					// rare, but ... can we do some guesstimate for this year?
				}
			}
		}

		double totalCount = 0.0d;
		for (int year : yearCounts.keySet())
			totalCount += yearCounts.get(year);

		if (cutOffThreshold != 0.0 && cutOffThreshold < 1.0) {
			double cutOff = totalCount * cutOffThreshold;

			if (yearCounts.size() > 0) {
				int minYear = Collections.min(yearCounts.keySet());
				int maxYear = Collections.max(yearCounts.keySet());

				double cutAmount = 0.0;
				while (!yearCounts.isEmpty() && minYear < maxYear) {
					if (!yearCounts.containsKey(minYear)) {
						minYear += 1;
						continue;
					}

					cutAmount += yearCounts.get(minYear);
					if (cutAmount > cutOff)
						break;

					yearCounts.remove(minYear);
					minYear += 1;
				}
			}

			if (yearCounts.size() > 0) {
				int minYear = Collections.min(yearCounts.keySet());
				int maxYear = Collections.max(yearCounts.keySet());

				double cutAmount = 0.0;
				while (!yearCounts.isEmpty() && maxYear > minYear) {
					if (!yearCounts.containsKey(maxYear)) {
						maxYear -= 1;
						continue;
					}

					cutAmount += yearCounts.get(maxYear);
					if (cutAmount > cutOff)
						break;
					yearCounts.remove(maxYear);
					maxYear -= 1;
				}
			}
		}

		return yearCounts;
	}

	public Map<String, List<WikipediaDateExtractor.WeigthtedActivity>> getActivityProfile() {

		if (getBirthdate() == null)
			return null;

		int birthYear = getBirthdate().get(Calendar.YEAR);
		int deathYear = getDeathdate().get(Calendar.YEAR);

		Map<Integer, Double> yearCounts = getYearCounts(birthYear, deathYear);

		return getMostWeightedPeriods(birthYear, deathYear, yearCounts);
	}

	private Map<String, List<WikipediaDateExtractor.WeigthtedActivity>> getMostWeightedPeriods(
			int birthYear, int deathYear, Map<Integer, Double> yearCounts) {

		java.util.Map<String, List<WikipediaDateExtractor.WeigthtedActivity>> weightedPeriods = new HashMap<String, List<WeigthtedActivity>>();

		for (String weightMethod : weightFuncs.keySet()) {
			List<WikipediaDateExtractor.WeigthtedActivity> ret = new ArrayList<WikipediaDateExtractor.WeigthtedActivity>();

			Double[] mentions = new Double[deathYear - birthYear + 1];
			Double[] cumMentions = new Double[deathYear - birthYear + 1];
			Double totalMentions = 0.0d;

			for (int year = birthYear; year < deathYear + 1; ++year) {
				int idx = year - birthYear;
				mentions[idx] = yearCounts.containsKey(year) ? yearCounts
						.get(year) : 0;

				cumMentions[idx] = 0.0d;
				totalMentions += mentions[idx];
			}

			for (int gap = 0; gap < deathYear - birthYear; ++gap) {
				double maxRatio = 0;
				int maxRatioYearBegin = -1;
				int maxRatioYearEnd = -1;

				for (int year = birthYear; year < deathYear - gap + 1; ++year) {
					int idx = year - birthYear;

					cumMentions[idx] += mentions[idx + gap];

					double ratio = (double) cumMentions[idx]
							* weightFuncs.get(weightMethod).getWeight(year,
									year + gap + 1, cumMentions[idx],
									totalMentions);

					if (ratio > maxRatio) {
						maxRatio = ratio;
						maxRatioYearBegin = birthYear + idx;
						maxRatioYearEnd = maxRatioYearBegin + gap;
					}
				}

				// we now have the highest mentions ratio for this gap period
				if (maxRatio > 0) {
					ret.add(new WeigthtedActivity(maxRatio, gap,
							maxRatioYearBegin, maxRatioYearEnd));
				}
			}

			Collections.sort(ret);
			Collections.reverse(ret);

			// TODO: hardcoded top 3 ratioed periods
			if (!ret.isEmpty())
				weightedPeriods.put(weightMethod,
						ret.subList(0, Math.min(3, ret.size())));
		}

		return weightedPeriods;
	}

	private GregorianCalendar getPersonInfoBoxDate(String key) {
		if (!isEntity()) {
			// throw Exception?
			return null;
		}

		return parseDateFromInfoboxLine(key);
	}

	private GregorianCalendar parseDateFromInfoboxLine(String line) {
		Matcher matcher = INFOBOX_DATE_PATTERN.matcher(line);

		if (!matcher.find()) {
			return null;
		}

		String[] dateParts = StringUtils.split(matcher.group(), '|');

		if (matcher.find()) {
			// no op, but this means that we found another date ... ?
		}

		if (dateParts.length >= 3) {
			return new GregorianCalendar(Integer.parseInt(dateParts[0]),
					Integer.parseInt(dateParts[1]),
					Integer.parseInt(dateParts[2]));
		} else {
			return null;
		}
	}

	private static final String[] INVALID_SECTIONS = { "== References ==",
			"== Notes ==", "== External links ==" };

	private static final Pattern SECTION_LEVEL_HEADING_PATTERN = Pattern
			.compile("\\=\\=\\s+[^\\=]+\\s+\\=\\=");

	public static String removeInvalidSections(String content) {
		for (String invalidSection : INVALID_SECTIONS) {
			content = removeSection(content, invalidSection);
		}

		return content;
	}

	private static String removeSection(String content, String sectionToRemove) {
		Matcher headings = SECTION_LEVEL_HEADING_PATTERN.matcher(content);

		int sectionHeaderLoc = content.length();
		int postSectionHeaderLoc = content.length();

		while (headings.find()) {
			if (headings.group().trim().equals(sectionToRemove)) {
				sectionHeaderLoc = headings.start();
				break;
			}
		}

		if (headings.find()) {
			postSectionHeaderLoc = headings.start() - 1;
		}

		return content.substring(0, sectionHeaderLoc)
				+ content.substring(postSectionHeaderLoc);
	}

	public final class WeigthtedActivity implements
			Comparable<WeigthtedActivity> {
		private final double mentionsRatio;
		private final int numberOfYears;
		private final int beginYear;
		private final int endYear;

		public WeigthtedActivity(double mentionsRatio, int numberOfYears,
				int beginYear, int endYear) {
			super();
			this.mentionsRatio = mentionsRatio;
			this.numberOfYears = numberOfYears;
			this.beginYear = beginYear;
			this.endYear = endYear;
		}

		public double getMentionsRatio() {
			return mentionsRatio;
		}

		public int getNumberOfYears() {
			return numberOfYears;
		}

		public int getBeginYear() {
			return beginYear;
		}

		public int getEndYear() {
			return endYear;
		}

		@Override
		public int compareTo(WeigthtedActivity o) {
			double difference = this.mentionsRatio - o.mentionsRatio;
			if (difference < 0)
				return -1;

			// ignoring equal ratios
			return 1;
		}
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		WikipediaDateExtractor wde = new WikipediaDateExtractor(
				StringUtils.EMPTY, StringUtils.EMPTY);

		System.out
				.println(wde
						.parseDateFromInfoboxLine("| birth_date               = {{birth date and age|1974|10|15|df=y}}"));

		String[] dateStrings = { "dsda on 12th September 1290",
				"in March 2003, he ran", "in 1984", "the 15th of August, 1985" };

		for (String dateString : dateStrings) {
			Matcher matcher = DATE_PATTERN.matcher(dateString);

			System.out.println("looking in '" + dateString + "'");
			while (matcher.find()) {
				System.out.println("Date: " + matcher.group());
				System.out.println("found year "
						+ matcher.group(matcher.groupCount()));
			}
		}

		System.out.println(DATE_REGEX);
	}

	public double getLifeYearsMentions() {
		double total = 0;

		if (getBirthdate() == null)
			return total;

		Integer birthYear = getBirthdate().get(Calendar.YEAR);
		Integer deathYear = getDeathdate().get(Calendar.YEAR);

		Map<Integer, Double> yearCounts = getYearCounts(birthYear, deathYear);

		for (int year : yearCounts.keySet())
			total += yearCounts.get(year);

		return total;
	}

	public List<Integer> getAgeProfile() {

		if (getBirthdate() == null)
			return null;

		Integer birthYear = getBirthdate().get(Calendar.YEAR);
		Integer deathYear = getDeathdate().get(Calendar.YEAR);

		boolean oldAdjustYearWeights = adjustYearWeights;
		adjustYearWeights = false;

		Map<Integer, Double> yearCounts = getYearCounts(birthYear, deathYear);
		double total = getLifeYearsMentions();

		List<Integer> ageProfile = new ArrayList<Integer>();
		for (int year = birthYear; year < deathYear; ++year) {
			ageProfile
					.add(yearCounts.containsKey(year) ? (int) ((double) yearCounts
							.get(year)) : 0);
		}

		return ageProfile;
	}

}
